use std::sync::mpsc;

use async_std::task::{self, AccessError};
use async_std::task_local;

use crate::record;
use crate::TargetMapping;

use super::global::LOGGER_GLOBAL;
use super::Message;

pub struct LoggerTaskLocal {
    pub message_sender: mpsc::Sender<Message>,
    pub tag: String,
    last_sec: std::cell::Cell<i64>,
    formated_sec_time: std::cell::RefCell<String>,
}

impl LoggerTaskLocal {
    #[allow(dead_code)]
    pub fn record_log<T: TargetMapping>(&self, record: &log::Record) -> crate::Result<()> {
        let target = T::map(record.metadata().target());
        let file = record.file_static().ok_or("record file is None")?;
        let time = chrono::Local::now();
        let cur_secs = time.timestamp();
        if cur_secs > self.last_sec.get() {
            *self.formated_sec_time.borrow_mut() = time.format("%Y%m%d-%H:%M:%S").to_string();
            self.last_sec.set(cur_secs);
        }
        let content = format!(
            "[{}.{:03}] {} - {:<80} --- {} {}:{}\n",
            *self.formated_sec_time.borrow(),
            time.timestamp_subsec_millis(),
            record.level(),
            record.args(),
            self.tag,
            file,
            record.line().ok_or_else(|| "record line is None")?,
        );
        self.message_sender
            .send(Message::Record(record::Record::new(
                target as u8,
                time,
                content,
            )))?;
        Ok(())
    }
}

task_local! {
    static LOGGER_TASK_LOCAL: LoggerTaskLocal = {
        LoggerTaskLocal {
            message_sender: LOGGER_GLOBAL.message_sender.lock().unwrap().clone(),
            tag: match task::current().name() {
                Some(ref name) => format!("task:{}", name.to_string()),
                None => format!("task:{:?}", task::current().id()),
            },
            last_sec: std::cell::Cell::default(),
            formated_sec_time: std::cell::RefCell::default(),
        }
    }
}

pub fn try_with_task_local<F, R>(f: F) -> Result<R, AccessError>
where
    F: FnOnce(&LoggerTaskLocal) -> R,
{
    LOGGER_TASK_LOCAL.try_with(|local| f(local))
}
